#include "socket_glue_code.h"
#include <stdio.h>
#include <stdlib.h>


#ifdef WIN32
#include <windows.h>
typedef unsigned long int pthread_t;
void pthread_exit(void *value_ptr){
    //Not needed in C++ Windows code, just return from the function
}

int pthread_create(pthread_t * thread,
                   void * thread_attr_null,
                   LPTHREAD_START_ROUTINE start_routine, void * arg){
DWORD  threadId;
CreateThread(NULL, 0, start_routine, arg, 0, &threadId);
return threadId;
}

int pthread_detach(pthread_t thread){
    return 0;
    //we do nothing
}
#else
DEFINE WINAPI
    typedef unsigned long DWORD
#endif


void error(char* msg){
  perror(msg);
  exit(1);
}

int sockfd;
int portno=80;
struct sockaddr_in server_addr;

void init(void){
  startWin32();
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd<0) error("ERROR open");
  
  memset((char*) &server_addr,0, sizeof(server_addr));
  
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(portno);
  
  if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) <0)
    error("ERROR bind");
  
  listen(sockfd,5);
}
enum parserStatus {
  Start, G, E, T, ReadURL, Complete, Fail
};

enum parserStatus parseRequest(char* buffer,int charsRead, char** URL){
  char* next;
  next = buffer;
  int urlMaxSize=128;
  int urlSize=0;
  *URL = (char*) malloc(urlMaxSize);
  (*URL)[0]='\0';
  enum parserStatus ps = Start;
  
#define transition(from,with,to) case from: if(*next==with) ps=to; else ps=Fail; break;
  do{
      if((next-buffer)>charsRead){
	ps=Fail;
      }else{
	switch(ps){
	  transition(Start,'G',G)
	  transition(G,'E',E)
	  transition(E,'T',T)
	  transition(T,' ',ReadURL)
	  case ReadURL:
	    if(*next==' '){
	      *URL = (char*) realloc(*URL,urlSize+1);
	      ps=Complete;
	    }else{
	      if(urlSize>=(urlMaxSize-1)){
		urlMaxSize+=128;
		*URL = (char*) realloc(*URL,urlMaxSize);
	      }
	      (*URL)[urlSize]=*next;
	      urlSize++;
	      (*URL)[urlSize]='\0';
	    }
	    break;
	  case Complete:
	  case Fail:
	    break;
	}
	next++;
      }
  }while(ps!=Complete && ps!=Fail);
  
  return ps;
}
DWORD WINAPI handleConnection(void* args){
  int socketToClient = *(int*)args;
  free(args);
  
  char buffer[2048];
  char* URL;
  
  int charsRead = read(socketToClient,buffer,2048);
  enum parserStatus ps = parseRequest(buffer, charsRead,&URL);
  
  

  if(ps==Fail){
    free(URL);
    char* return400 =	"HTTP/1.0 400 Bad Request\r\n"
			"Server: Fail a lot\r\n"
			"Connection: close\r\n"
			"\r\n"
			"400";
    write(socketToClient,return400,strlen(return400));
    close(socketToClient);
    pthread_exit(NULL);
    return 0;
  }
  
  
  
  FILE* file = fopen(URL+1,"r");
  if(file==NULL){
    free(URL);
    char* return404 =	"HTTP/1.0 404 File not found\r\n"
			"Server: Fail a lot\r\n"
			"Connection: close\r\n"
			"\r\n"
			"404";
    write(socketToClient,return404,strlen(return404));
    close(socketToClient);
    pthread_exit(NULL);
    return 0;
  }
  
  char* returnokay =	"HTTP/1.0 200 OK\r\n"
			"Server: Fail a lot\r\n"
			"Connection: close\r\n"
			"\r\n";
			
  write(socketToClient,returnokay,strlen(returnokay));
  
  char fromFile;
  for(fromFile=fgetc(file);fromFile!=EOF;fromFile=fgetc(file)){
    write(socketToClient,&fromFile,1);
  }
  close(socketToClient);
  free(URL);
  fclose(file);
  pthread_exit(NULL);
  return 0;
}

void waitForConnection(void){
  int newsockfd;
  struct sockaddr_in cli_addr;
  
  socklen_t clilen = sizeof(cli_addr);
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if(newsockfd<0) error("ERROR accept");
  pthread_t th;
  int* threadarg = (int*) malloc(sizeof(int));
  *threadarg = newsockfd;
  int ret = pthread_create(&th,NULL,handleConnection,threadarg);
  if(ret <0){
    free(threadarg);
  }else{
   pthread_detach(th);
  }

}

int main(void){
  init();
  while(1){
    waitForConnection();
  }
  
  close(sockfd);
  return 0;
}
